﻿using Microsoft.Extensions.Options;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace Xmart.Expressions.DynamicPredicate;

public class DefaultDynamicPredicateBuilder : IDynamicPredicateBuilder
{
    protected IServiceProvider ServiceProvider { get; }
    protected IOptions<DynamicPredicateOptions> Options { get; }
    public DefaultDynamicPredicateBuilder(IServiceProvider serviceProvider, IOptions<DynamicPredicateOptions> options)
    {
        ServiceProvider = serviceProvider;
        Options = options;
    }

    /// <summary>
    /// 构建动态条件为表达式树 
    /// </summary>
    /// <typeparam name="T">参数类型</typeparam>
    /// <param name="model">动态条件模型</param>
    /// <param name="extensions">扩展实现</param>
    /// <param name="propertiesCache">属性缓存</param>
    /// <param name="cancellationToken">撤销令牌</param>
    /// <returns></returns>
    public virtual async Task<Expression<Func<T, bool>>> BuildAsync<T>(
        DynamicPredicateModel model,
        IDynamicPredicateExtensions? extensions = null,
        Dictionary<string, PropertyInfo>? propertiesCache = null,
        CancellationToken cancellationToken = default)
    {
        return (Expression<Func<T, bool>>)await BuildAsync(typeof(T), model, extensions, propertiesCache, cancellationToken);
    }

    /// <summary>
    /// 构建动态条件为表达式树 
    /// </summary>
    /// <param name="parameterType">参数类型</param>
    /// <param name="model">动态条件模型</param>
    /// <param name="extensions">扩展实现</param>
    /// <param name="propertiesCache">属性缓存</param>
    /// <param name="cancellationToken">撤销令牌</param>
    /// <returns></returns>
    public virtual async Task<LambdaExpression> BuildAsync(
        Type parameterType,
        DynamicPredicateModel model,
        IDynamicPredicateExtensions? extensions = null,
        Dictionary<string, PropertyInfo>? propertiesCache = null,
        CancellationToken cancellationToken = default)
    {
        extensions ??= ServiceProvider.GetService<IDynamicPredicateExtensions>();
        propertiesCache ??= [];
        if (model.Type == DynamicPredicateType.Predicate)
        {
            if (model.Predicate == null)
            {
                throw new DynamicPredicateException("无法构建表达式树");
            }
            return await BuildAsync(parameterType, model.Predicate, propertiesCache, extensions, cancellationToken);
        }

        if (model.Group == null)
        {
            throw new DynamicPredicateException("无法构建表达式树");
        }
        model = model.Defrag();
        var v = model.Relation == DynamicPredicateRelation.And;
        LambdaExpression? where = null;

        foreach (var r in model.Group!)
        {
            var expression = await BuildAsync(parameterType, r, extensions, propertiesCache, cancellationToken);
            if (where == null)
            {
                where = expression;
            }
            else
            {
                if (v)
                {
                    where = LambdaHelpers.And(where, expression);
                }
                else
                {
                    where = LambdaHelpers.Or(where, expression);
                }
            }
        }

        if (where == null)
        {
            var parameter = Expression.Parameter(parameterType, "p");
            var body = Expression.Constant(v);
            where = Expression.Lambda(body, parameter);
        }
        return where;
    }

    /// <summary>
    /// 构建动态条件项为表达式树
    /// </summary>
    /// <param name="parameterType">参数类型</param>
    /// <param name="item">动态条件项</param>
    /// <param name="propertiesCache">属性缓存</param>
    /// <param name="extensions">扩展</param>
    /// <param name="cancellationToken">撤销令牌</param>
    /// <returns></returns>
    protected virtual async Task<LambdaExpression> BuildAsync(
        Type parameterType,
        DynamicPredicateItem item,
        Dictionary<string, PropertyInfo> propertiesCache,
        IDynamicPredicateExtensions? extensions = null,
        CancellationToken cancellationToken = default)
    {
        var parameter = Expression.Parameter(parameterType, "p");
        var (propertyInfo, leftExpression) = GetPropertyAndLeftMemberExpression(
            parameterType,
            parameter,
            item.Property,
            propertiesCache);

        var isNullable = false;
        var propertyType = propertyInfo.PropertyType;
        if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            var t = propertyType.GetGenericArguments()[0];
            if (!Options.Value.ValueTypes.Contains(t))
            {
                throw new DynamicPredicateException($"[{t.Name}]值类型属性[{item.Property}]不支持动态条件构建表达式树");
            }
            propertyType = t;
            isNullable = true;
        }
        var reflectedType = propertyInfo.ReflectedType;
        var isCollection = propertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(propertyType)
            && propertyType.GetInterfaces().Any(r => r.IsGenericType && r.Name.StartsWith(nameof(IEnumerable)));
        if (!isCollection && item.CompareType != CompareType.Custom && (!Options.Value.CompareTypeSupportPropertyTypes.TryGetValue(item.CompareType, out var ctspts) || !ctspts.Contains(propertyType) && (!propertyType.IsEnum || !ctspts.Contains(typeof(Enum)))))
        {
            throw new DynamicPredicateException($"[{propertyType.Name}]类型的属性[{item.Property}]暂不支持动态条件构建表达式树");
        }
        if (isCollection)
        {
            propertyType = propertyType.GetInterfaces()
                .FirstOrDefault(r => r.IsGenericType && r.Name.StartsWith(nameof(IEnumerable))).GetGenericArguments()[0];
        }
        if (isCollection && (item.CompareType == CompareType.ItemAny || item.CompareType == CompareType.ItemAll))
        {
            if (item.ItemPredicateModel == null)
            {
                throw new DynamicPredicateException("数据有误，无法构建表达式树");
            }
            var itemExp = await BuildAsync(propertyType, item.ItemPredicateModel, extensions, propertiesCache, cancellationToken);
            var methodName = item.CompareType.ToString()[4..];
            var method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == methodName && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
            var constant = Expression.Call(method, leftExpression, itemExp);
            return Expression.Lambda(constant, parameter);
        }
        else if (isCollection && item.AggregateType.HasValue)
        {
            if (!Options.Value.NumberTypeCompareTypes.Contains(item.CompareType))
            {
                throw new DynamicPredicateException("数据有误，无法构建表达式树");
            }

            var itemType = propertyType;
            var itemPropertyType = itemType;
            var itemPropertyParamExpr = Expression.Parameter(itemPropertyType, "i");
            Expression itemPropertyBodyExpr;
            if (!string.IsNullOrWhiteSpace(item.AggregateProperty))
            {
                PropertyInfo itemValueProperty;
                (itemValueProperty, itemPropertyBodyExpr) = GetPropertyAndLeftMemberExpression(itemType, itemPropertyParamExpr, item.AggregateProperty, propertiesCache);
                itemPropertyType = itemValueProperty.PropertyType;
            }
            else
            {
                itemPropertyBodyExpr = itemPropertyParamExpr;
            }

            if (item.ItemPredicateModel != null)
            {
                var itemExp = await BuildAsync(itemType, item.ItemPredicateModel, extensions, propertiesCache, cancellationToken);
                var whereMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == "Where" && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(itemType) : itemType);
                leftExpression = Expression.Call(whereMethod, leftExpression, itemExp);
            }
            if (item.AggregateType == AggregateType.Count)
            {
                var method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == "LongCount" && r.GetParameters().Length == 1).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(itemType) : itemType);
                leftExpression = Expression.Call(method, leftExpression);
                propertyType = typeof(long);
            }
            else
            {
                Expression itemPropertyExpr = Expression.Lambda(itemPropertyBodyExpr, itemPropertyParamExpr);

                MethodInfo? method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == item.AggregateType.Value.ToString() && r.GetParameters().Length == 2 && r.GetParameters()[1].ParameterType.GetGenericArguments()[1] == itemPropertyType).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(itemType) : itemType);

                leftExpression = Expression.Call(method, leftExpression, itemPropertyExpr);
                propertyType = itemPropertyType;
            }
        }
        else if (isCollection
            && item.CompareType != CompareType.Contains
            && item.CompareType != CompareType.NotContains
            && item.CompareType != CompareType.HasIntersection
            && item.CompareType != CompareType.NotHasIntersection
            && item.CompareType != CompareType.AllIn
            && item.CompareType != CompareType.AllNotIn
            && item.CompareType != CompareType.ContainsAll)
        {
            throw new DynamicPredicateException($"集合属性[{item.Property}]不支持[{item.CompareType}]比较方式的动态条件构建表达式树");
        }

        Expression body = await BuildBodyExpressionAsync(item, reflectedType, propertyType, isNullable, leftExpression, extensions, cancellationToken);

        var lambda = Expression.Lambda(body, parameter);
        return lambda;
    }

    /// <summary>
    /// 构建Body表达式树
    /// </summary>
    /// <param name="item">动态条件项</param>
    /// <param name="propertyReflectedType">属性所属实例类型</param>
    /// <param name="propertyType">属性类型</param>
    /// <param name="isNullable">是否可空</param>
    /// <param name="memberExpression">成员表达式</param>
    /// <param name="extensions">扩展</param>
    /// <param name="cancellationToken">撤销令牌</param>
    /// <returns></returns>
    /// <exception cref="DynamicPredicateException"></exception>
    protected virtual async Task<Expression> BuildBodyExpressionAsync(
        DynamicPredicateItem item,
        Type propertyReflectedType,
        Type propertyType,
        bool isNullable,
        Expression memberExpression,
        IDynamicPredicateExtensions? extensions = null,
        CancellationToken cancellationToken = default)
    {
        MethodInfo method;
        Expression valExp;
        IList vals = null!;
        Expression body;
        if (Options.Value.Replacements.TryGetValue((propertyReflectedType, item.Property, item.CompareType), out var func))
        {
            body = func(item);
            return body;
        }
        var strType = typeof(string);
        var zeroExp = Expression.Constant(0);
        var nullExp = Expression.Constant(null);
        switch (item.CompareType)
        {
            case CompareType.HasValue:
                valExp = nullExp;
                body = Expression.NotEqual(memberExpression, valExp);
                break;
            case CompareType.NotHasValue:
                valExp = nullExp;
                body = Expression.Equal(memberExpression, valExp);
                break;
            case CompareType.Empty:
                method = strType.GetMethod(nameof(string.IsNullOrEmpty), BindingFlags.Static | BindingFlags.Public);
                body = Expression.Call(method, memberExpression);
                break;
            case CompareType.NotEmpty:
                method = strType.GetMethod(nameof(string.IsNullOrEmpty), BindingFlags.Static | BindingFlags.Public);
                body = Expression.Not(Expression.Call(method, memberExpression));
                break;
            case CompareType.IsTrue:
                valExp = ConvertValue(propertyType, true, isNullable);
                body = Expression.Equal(memberExpression, valExp);
                break;
            case CompareType.IsFalse:
                valExp = ConvertValue(propertyType, false, isNullable);
                body = Expression.Equal(memberExpression, valExp);
                break;
            case CompareType.Equals:
                if (propertyType == strType)
                {
                    valExp = Expression.Constant(item.Value);
                    body = Expression.Equal(memberExpression, valExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.Equal(memberExpression, valExp);
                }
                break;
            case CompareType.NotEquals:
                if (propertyType == strType)
                {
                    valExp = Expression.Constant(item.Value);
                    body = Expression.NotEqual(memberExpression, valExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.NotEqual(memberExpression, valExp);
                }
                break;
            case CompareType.GreaterThan:
                if (propertyType == strType)
                {
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    valExp = Expression.Constant(item.Value);
                    body = Expression.GreaterThan(Expression.Call(method, memberExpression, valExp), zeroExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.GreaterThan(memberExpression, valExp);
                }
                break;
            case CompareType.GreaterThanOrEqual:
                if (propertyType == strType)
                {
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    valExp = Expression.Constant(item.Value);
                    body = Expression.GreaterThanOrEqual(Expression.Call(method, memberExpression, valExp), zeroExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.GreaterThanOrEqual(memberExpression, valExp);
                }
                break;
            case CompareType.LessThan:
                if (propertyType == strType)
                {
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    valExp = Expression.Constant(item.Value);
                    body = Expression.LessThan(Expression.Call(method, memberExpression, valExp), zeroExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.LessThan(memberExpression, valExp);
                }
                break;
            case CompareType.LessThanOrEqual:
                if (propertyType == strType)
                {
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    valExp = Expression.Constant(item.Value);
                    body = Expression.LessThanOrEqual(Expression.Call(method, memberExpression, valExp), zeroExp);
                }
                else
                {
                    valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.LessThanOrEqual(memberExpression, valExp);
                }
                break;
            case CompareType.Contains:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                if (propertyType == strType)
                {
                    method = strType.GetMethod(nameof(string.Contains), [strType]);
                    body = Expression.Call(memberExpression, method, valExp);
                }
                else
                {
                    method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(propertyType);
                    body = Expression.Call(method, memberExpression, valExp);
                }
                break;
            case CompareType.NotContains:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                if (propertyType == strType)
                {
                    method = strType.GetMethod(nameof(string.Contains), [strType]);
                    body = Expression.Not(Expression.Call(memberExpression, method, valExp));
                }
                else
                {
                    method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                    body = Expression.Not(Expression.Call(method, memberExpression, valExp));
                }
                break;
            case CompareType.StartsWith:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = strType.GetMethod(nameof(string.StartsWith), [strType]);
                body = Expression.Call(memberExpression, method, valExp);
                break;
            case CompareType.NotStartsWith:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = strType.GetMethod(nameof(string.StartsWith), [strType]);
                body = Expression.Not(Expression.Call(memberExpression, method, valExp));
                break;
            case CompareType.EndsWith:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = strType.GetMethod(nameof(string.EndsWith), [strType]);
                body = Expression.Call(memberExpression, method, valExp);
                break;
            case CompareType.NotEndsWith:
                valExp = await ParseToValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = strType.GetMethod(nameof(string.EndsWith), [strType]);
                body = Expression.Not(Expression.Call(memberExpression, method, valExp));
                break;
            case CompareType.Between:
                if (propertyType == strType)
                {
                    vals = item.Value.Split(',');
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    var val0Exp = Expression.Constant(vals[0]);
                    var val1Exp = Expression.Constant(vals[1]);
                    var betweenLeft = Expression.GreaterThanOrEqual(Expression.Call(method, memberExpression, val0Exp), zeroExp);
                    var betweenRight = Expression.LessThanOrEqual(Expression.Call(method, memberExpression, val1Exp), zeroExp);
                    body = Expression.And(betweenLeft, betweenRight);
                }
                else
                {
                    vals = await ParseToListValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.And(Expression.GreaterThanOrEqual(memberExpression, ConvertValue(propertyType, vals[0], isNullable)), Expression.LessThanOrEqual(memberExpression, ConvertValue(propertyType, vals[1], isNullable)));
                }
                break;
            case CompareType.NotBetween:
                if (propertyType == strType)
                {
                    vals = item.Value.Split(',');
                    method = strType.GetMethods(BindingFlags.Static | BindingFlags.Public)
                        .First(r => r.Name == nameof(string.Compare) && r.GetParameters().Length == 2);
                    var val0Exp = Expression.Constant(vals[0]);
                    var val1Exp = Expression.Constant(vals[1]);
                    var betweenLeft = Expression.GreaterThanOrEqual(Expression.Call(method, memberExpression, val0Exp), zeroExp);
                    var betweenRight = Expression.LessThanOrEqual(Expression.Call(method, memberExpression, val1Exp), zeroExp);
                    body = Expression.Not(Expression.And(betweenLeft, betweenRight));
                }
                else
                {
                    vals = await ParseToListValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                    body = Expression.Not(Expression.And(Expression.GreaterThanOrEqual(memberExpression, ConvertValue(propertyType, vals[0], isNullable)), Expression.LessThanOrEqual(memberExpression, ConvertValue(propertyType, vals[1], isNullable))));
                }
                break;
            case CompareType.In:
                vals = await ParseToListValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                body = Expression.Call(method, Expression.Constant(vals), memberExpression);
                break;
            case CompareType.NotIn:
                vals = await ParseToListValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                body = Expression.Not(Expression.Call(method, Expression.Constant(vals), memberExpression));
                break;
            case CompareType.BelongTo:
                if (extensions == null)
                {
                    throw new DynamicPredicateException("不支持属于比较");
                }
                vals = await extensions.GetBelongToListAsync(propertyReflectedType, item.Property, item.Value, cancellationToken);
                method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                body = Expression.Call(method, Expression.Constant(vals), memberExpression);
                break;
            case CompareType.NotBelongTo:
                if (extensions == null)
                {
                    throw new DynamicPredicateException("不支持不属于比较");
                }
                vals = await extensions.GetBelongToListAsync(propertyReflectedType, item.Property, item.Value, cancellationToken);
                method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                body = Expression.Not(Expression.Call(method, Expression.Constant(vals), memberExpression));
                break;
            case CompareType.RegexMatch:
                valExp = ConvertValue(propertyType, new Regex(item.Value), isNullable);
                method = typeof(Regex).GetMethod(nameof(Regex.IsMatch), [strType]);
                body = Expression.Call(valExp, method, memberExpression);
                break;
            case CompareType.NotRegexMatch:
                valExp = ConvertValue(propertyType, new Regex(item.Value), isNullable);
                method = typeof(Regex).GetMethod(nameof(Regex.IsMatch), [strType]);
                body = Expression.Not(Expression.Call(valExp, method, memberExpression));
                break;
            case CompareType.HasIntersection:
            case CompareType.NotHasIntersection:
            case CompareType.AllIn:
            case CompareType.AllNotIn:
            case CompareType.ContainsAll:
                var methodName = new[] { CompareType.HasIntersection, CompareType.NotHasIntersection }.Contains(item.CompareType) ? "Any" : "All";
                vals = await ParseToListValueTypeAsync(propertyType, isNullable, item.Value, extensions, cancellationToken);
                method = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == methodName && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                var method2 = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(r => r.Name == nameof(Enumerable.Contains) && r.GetParameters().Length == 2).MakeGenericMethod(isNullable ? typeof(Nullable<>).MakeGenericType(propertyType) : propertyType);
                Expression p1 = memberExpression;
                Expression p2 = Expression.Constant(vals);
                if (item.CompareType == CompareType.ContainsAll)
                {
                    p1 = Expression.Constant(vals);
                    p2 = memberExpression;
                }
                var ps = Expression.Parameter(propertyType, "ps");
                Expression body2 = Expression.Call(method2, p2, ps);
                if (item.CompareType == CompareType.AllNotIn)
                {
                    body2 = Expression.Not(body2);
                }
                var lam = Expression.Lambda(body2, ps);
                body = Expression.Call(method, p1, lam);
                if (item.CompareType == CompareType.NotHasIntersection)
                {
                    body = Expression.Not(body);
                }
                break;
            default:
                body = await GetCustomPredicateAsync(item, propertyReflectedType, propertyType, memberExpression, extensions, cancellationToken) ?? throw new DynamicPredicateException("不支持自定义比较符");
                break;
        }

        return body;
    }

    protected virtual Expression ConvertValue(Type type, object value, bool isNullable)
    {
        if (isNullable)
        {
            return Expression.Convert(Expression.Constant(value), typeof(Nullable<>).MakeGenericType(type));
        }
        return Expression.Constant(value);
    }

    protected virtual async Task<Expression?> GetCustomPredicateAsync(
        DynamicPredicateItem item,
        Type propertyReflectedType,
        Type propertyType,
        Expression memberExpression,
        IDynamicPredicateExtensions? extensions,
        CancellationToken cancellationToken = default)
    {
        if (extensions == null)
        {
            return null;
        }
        return await extensions.GetCustomPredicateAsync(item, propertyReflectedType, propertyType, memberExpression, cancellationToken);
    }

    protected Expression GetPropertyOrField(Expression exp, string propertyName)
    {
        if (propertyName.Contains("."))
        {
            var ps = propertyName.Split('.');
            foreach (var p in ps)
            {
                exp = GetPropertyOrField(exp, p);
            }
            return exp;
        }
        return Expression.PropertyOrField(exp, propertyName);
    }

    protected virtual (PropertyInfo, Expression) GetPropertyAndLeftMemberExpression(
        Type type,
        Expression memberExpression,
        string propertyName,
        Dictionary<string, PropertyInfo> propertiesCache,
        string? propertyNamePrefix = null,
        CancellationToken cancellationToken = default)
    {
        //TODO:处理表达式的左侧子句Where的逻辑
        var fpn = string.IsNullOrWhiteSpace(propertyNamePrefix) ? propertyName
            : (propertyNamePrefix.Trim('.') + "." + propertyName);
        if (propertiesCache.TryGetValue(fpn, out var pi))
        {
            return (pi, GetPropertyOrField(memberExpression, propertyName));
        }
        var ps = propertyName.Split('.');

        var cp = ps[0].TrimEnd(']').TrimEnd('[');
        var isCollection = ps[0].EndsWith("[]");

        fpn = string.IsNullOrWhiteSpace(propertyNamePrefix) ? cp : (propertyNamePrefix.Trim('.') + "." + cp);

        if (!propertiesCache.TryGetValue(fpn, out pi))
        {
            pi = type.GetProperty(cp);
            if (pi == null)
            {
                throw new DynamicPredicateException($"属性{cp}在类型{type.FullName}中不存在");
            }
            propertiesCache[fpn] = pi;
        }

        var me = GetPropertyOrField(memberExpression, cp);
        if (ps.Length > 1)
        {
            var propertyType = isCollection ? pi.PropertyType.GetInterfaces().First(r => r.Name.StartsWith("IEnumerable") && r.IsGenericType).GetGenericArguments()[0] : pi.PropertyType;
            return GetPropertyAndLeftMemberExpression(propertyType, me, propertyName[propertyName.IndexOf(".")..].TrimStart('.'), propertiesCache, isCollection ? (fpn + "[]") : fpn);
        }
        else
        {
            return (pi, me);
        }
    }

    protected virtual async Task<Expression> ParseToValueTypeAsync(
        Type type,
        bool isNullable,
        string value,
        IDynamicPredicateExtensions? extensions,
        CancellationToken cancellationToken = default)
    {
        var result = await ParseToValueTypeAsync(type, value, extensions, cancellationToken);
        return ConvertValue(type, result, isNullable);
    }

    protected virtual async Task<object> ParseToValueTypeAsync(
        Type type,
        string value,
        IDynamicPredicateExtensions? extensions,
        CancellationToken cancellationToken = default)
    {
        if (type == typeof(string))
        {
            return value;
        }
        var ex = new DynamicPredicateException($"无法将值[{value}]转换成[{type.FullName}]类型");
        if (type == typeof(Guid))
        {
            return Guid.TryParse(value, out var result) ? result : throw ex;
        }
        if (type == typeof(DateTime))
        {
            return DateTime.TryParse(value, out var result) ? result : throw ex;
        }
        if (type.IsEnum)
        {
            return Enum.Parse(type, value);
        }
        if (extensions != null)
        {
            var result = await extensions.ParseToValueTypeAsync(type, value, cancellationToken);
            if (result != null)
            {
                return result;
            }
        }
        try
        {
            return Convert.ChangeType(value, type);
        }
        catch
        {
            throw ex;
        }
    }

    protected virtual async Task<IList> ParseToListValueTypeAsync(
        Type type,
        bool isNullable,
        string value,
        IDynamicPredicateExtensions? extensions,
        CancellationToken cancellationToken = default)
    {
        var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(isNullable ? typeof(Nullable<>).MakeGenericType(type) : type));
        var vals = value.Split(',');
        foreach (var val in vals)
        {
            list.Add(await ParseToValueTypeAsync(type, val, extensions, cancellationToken));
        }
        return list;
    }
}
